/*  PrimeBlaster
    by Kjetil Eide 
    kjetile445@gmail.com

    This file is part of PrimeBlaster

    PrimeBlaster is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    PrimeBlaster is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with PrimeBlaster.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.eide.spaceship;

import java.util.ArrayList;
import java.util.Random;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Vector2f;
import org.newdawn.slick.util.FastTrig;
import org.newdawn.slick.Sound;

/*
 * Manage Walls, Asteroids and Spaceship objects 
 */

public class RBCollection {
	public static final int SHIP = 4; //index of ship in rbList
	public static final int EXPLOSION_XOFFSET = -160;
	public static final int EXPLOSION_YOFFSET = -120;
	
	
	private ArrayList<RigidBody> rbList;
	private Explosion explosion;
	private ImageData rbImage;
	private Collision collision;
	private Sound boom;
	private Sound rocks;
	
	public RBCollection(ImageData rbImage) throws SlickException {
		rbList = new ArrayList<RigidBody>();
		this.rbImage = rbImage;
		collision = new Collision();
		boom = new Sound("spacedata/bomb_explosion_2.ogg");
		rocks = new Sound("spacedata/rocks.ogg");
	}
	
	public void addWall(float x1, float y1, float x2, float y2, int type) {
		switch(type) {
		case 0:	rbList.add(new Wall(x1, y1, x2, y2, rbImage.getWallHorizontal()));
				break;
		case 1:	rbList.add(new Wall(x1, y1, x2, y2, rbImage.getWallVertical()));
				break;
		}
	}
	
	public void addShip(float x, float y) throws SlickException {
		rbList.add(new SpaceShip(100, 100, rbImage.getShipImage()));
	}
	
	public void addAsteroid(float orientation, Vector2f velocity, float x, float y, int type, int number) throws SlickException {

		rbList.add(new Asteroid(orientation, velocity, x, y,
								type, rbImage.getAsteroidImage(type), 
								number, rbImage.getNumberFont()));
	}
	
	public void addExplosion(float x, float y) {
		explosion = new Explosion(x, y, rbImage.getExplosionAnim());
	}
	
	public void add(RigidBody rb) {
		rbList.add(rb);		
	}
	
	public int getNumberOfRB() {
		return rbList.size();
	}
	
	public RigidBody getRB(int i) {
		if (i >= rbList.size()) {
			return null;
		} else {
			return rbList.get(i);
		}
	}	
	
	public Explosion getExplosion() {
		return explosion;
	}
	
	public SpaceShip getShip() {
		return (SpaceShip) rbList.get(SHIP);
	}
	
	public void remove(RigidBody rb) {
		rbList.remove(rb);
	}
	
	public void remove(int idx) {
		rbList.remove(idx);
	}
	
	public void removeAllAsteroids() {
		
		while(rbList.size() > SHIP + 1) {
			remove(SHIP+1);
		}
	}
	
	public void removeExplosion() {
		explosion = null;
	}
	
	
	public void generateRandomAsteroids(PlayerStatus playerStatus) throws SlickException {
		/* position asteroids randomly */
		Random rnd = new Random();
		boolean haveCollision;
		boolean allAsteroidsArePrime = true;
		
		for (int i = SHIP + 1; i < SHIP + 1 + playerStatus.getNumberOfAsteroids(); i++) {
			int number;
			boolean lastAsteroid;
			
			// place asteroids randomly on screen, making sure that two asteroids are not overlapping
			do {
				float x = rnd.nextInt(PrimeBlaster.SCREEN_WIDTH);
				float y = rnd.nextInt(PrimeBlaster.SCREEN_HEIGHT);
				
				/* chose a number between 2 and 163 */
				number = rnd.nextInt(12) + playerStatus.getLevel() * 11 - 2;		
				addAsteroid(0, new Vector2f(0,0), x, y, 0, number);
				haveCollision = collision.haveCollisions(this);
				if (haveCollision) {
					remove(i);
				} else {
					// Successfully placed an asteroid. Check if it is dividalble.
					if (!PrimeNumbers.isPrime(number)) {
						// yes. this means we have at least one dividable asteroid
						allAsteroidsArePrime = false;  
					}
				}
				lastAsteroid = (i == SHIP + playerStatus.getNumberOfAsteroids());
				//System.out.println("placed " + number);
				if (lastAsteroid) {
					//System.out.println("last asteroid is: " + number);
				}	
			} while (haveCollision || (lastAsteroid && allAsteroidsArePrime));			
		}
	}
	
	/* check to see if an asteroid should be destroyed, if it
	 * has to little power, or if it should be split in two
	 */
	public void updateAsteroidState(Asteroid asteroid, PlayerStatus playerStatus,
			float delta) throws SlickException {		
		
		
		if (asteroid != null) {
			
			float x = asteroid.getX();
			float y = asteroid.getY();
			
			AsteroidStates state = asteroid.updateState();
			if (state == AsteroidStates.DESTROY) {
				blowAsteroid(asteroid);
				playerStatus.evaluatePlayer(this);
			}
			
			else if (state == AsteroidStates.FAIL &&
					!getShip().isInvincible()) {
				getShip().reduceStrength(delta/5);		
			}
			
			else if (state == AsteroidStates.SPLIT) {
				boom.play();
				rocks.play();
				getShip().setInvincibleTimer(System.currentTimeMillis());
				getShip().setInvincible(true);
				float orientation = asteroid.getOrientation('R');
				float sinOrient = (float) FastTrig.sin(orientation);
				float cosOrient = (float) FastTrig.cos(orientation);
				
				int factor[] = PrimeNumbers.getFactors(asteroid.getDisplayNumber());
				
				int newAsteroidType = 0;
				
				float xOffset = 0;
				float yOffset = 0;
				
				switch(asteroid.getType()) {
				case 0: newAsteroidType = 1;
						xOffset = sinOrient * Asteroid.WIDTH[newAsteroidType] / 2;
						yOffset = cosOrient * Asteroid.HEIGHT[newAsteroidType] / 2;
						break;
				case 1: newAsteroidType = 3;
						xOffset = -cosOrient * Asteroid.WIDTH[newAsteroidType] / 2;
						yOffset = sinOrient * Asteroid.HEIGHT[newAsteroidType] / 2;
						break;
				case 2: newAsteroidType = 5;
						xOffset = -cosOrient * Asteroid.WIDTH[newAsteroidType] / 2;
						yOffset = sinOrient * Asteroid.HEIGHT[newAsteroidType] / 2;
						break;
				default: newAsteroidType = asteroid.getType();
						 xOffset = sinOrient * Asteroid.WIDTH[newAsteroidType] / 2;
						 yOffset = cosOrient * Asteroid.HEIGHT[newAsteroidType] / 2;
						 break;
 				}
				
				asteroid.setType(newAsteroidType, factor[0], rbImage.getAsteroidImage(newAsteroidType));
				asteroid.addPos(new Vector2f(xOffset, -yOffset));
				Vector2f velocity = new Vector2f(asteroid.getVelocity());
				velocity.negateLocal();
				addAsteroid(orientation, velocity, x-xOffset, y+yOffset, newAsteroidType+1, factor[1]);
				
				addExplosion(x+EXPLOSION_XOFFSET, y+EXPLOSION_YOFFSET);
				
				
//				switch(asteroid.getType()) {
//				case 0: asteroid.setType(1, factor[0], rbImage.getAsteroidImage(1));		
//						float xOffset = sinOrient * Asteroid.WIDTH[2] / 2;
//						float yOffset = cosOrient * Asteroid.WIDTH[2] / 2;
//						addAsteroid(orientation, x, y, 2, factor[1]);
//						
//						break;
//				case 1: asteroid.setType(3, factor[0], rbImage.getAsteroidImage(3));
//						addAsteroid(orientation, x2, y2, 4, factor[1]);
//						break;
//				case 2: asteroid.setType(5, factor[0], rbImage.getAsteroidImage(5));
//						addAsteroid(orientation, x2, y2, 6, factor[1]);
//						break;
//				}
				playerStatus.evaluatePlayer(this);
			}
		}
	}
	
	public void updateShipState(PlayerStatus ps) {
		if (((SpaceShip) rbList.get(SHIP)).getStrength() < 0) {
			blowShip();
			ps.setDead(true);
			ps.setlevelFinished(true);
		}
	}
	
	public void blowAsteroid(Asteroid asteroid) {
		float x = asteroid.getX();
		float y = asteroid.getY();
		remove(asteroid);
		addExplosion(x+EXPLOSION_XOFFSET, y+EXPLOSION_YOFFSET);
		boom.play();
	}
	
	public void blowShip() {
		addExplosion(rbList.get(SHIP).getX()+EXPLOSION_XOFFSET,
				 rbList.get(SHIP).getY()+EXPLOSION_YOFFSET);
		
		//addExplosion(rbList.get(SHIP).getX(),
		//		 rbList.get(SHIP).getY());
	
		/* move the ship outside the the screen to make it 
		 * disappear.
		 */
		rbList.get(SHIP).setPos(10000,10000);
		boom.play();
	}
	
	public void simulate(float timeStep) {
		for (int i = 0; i < getNumberOfRB(); i++) {
			getRB(i).translate();
			getRB(i).sumForces();
			getRB(i).calcAccel();
			getRB(i).integrate(timeStep);
		}				
	}
	
	public void doCollision() {
		collision.handleAllCollisions(this);
	}
}
